<?php

namespace app\index\service;

use app\common\job\ConsultingRushExp;
use app\common\job\LawyerResponseStatistics;
use app\common\model\Consulting;
use app\common\model\Member;
use app\common\service\SubscriptionMessageService;
use app\index\dao\MemberDao;
use app\index\traits\MatchRecorderTrait;
use ShidianUtil\Concurrent\Synchronizer;
use think\exception\ValidateException;
use think\facade\Config;
use think\facade\Queue;

/**
 * 咨询律师匹配 V1
 * 抢单 + 确认 + 返回公共池
 * Class ConsultingLawyerMatchService
 * @package app\consulting\service
 */
class ConsultingLawyerMatchService
{
    use MatchRecorderTrait;

    /**
     * 接单-系统随机匹配
     * @param Consulting $consulting
     * @return bool 已经匹配返回true 没有匹配返回false
     * @deprecated 准备废弃， 已经没有地方调用该匹配方式
     */
    public function matchRand(Consulting $consulting): bool
    {
        if (is_id($consulting->lawyerId)) {
            return true;
        }

        $now = time();

        // 获取律师
        $lawyer = $this->getRandLawyer($consulting);

        if (!$lawyer) {
            return false;
        }

        $lawyerId = $lawyer->id;
        $consultingId = $consulting->id;

        $success = false;

        // 随机匹配过期时间
        $expTime = Config::get('consulting.rand_time');

        $signature = Consulting::class . $consultingId;
        $this->getSynchronizer()->synchronized($signature, function () use ($consultingId, $lawyerId, $expTime, &$success) {

            $record = Consulting::where('id', $consultingId)->find();

            if (is_id($record->lawyerId)) {
                return;
            }

            if ($record->rushStatus == 2) {
                return;
            }

            $now = time();

            $record->lawyerId = $lawyerId;
            $record->rushTime = $now;
            $record->rushStatus = 1;
            $record->rushExpTime = $now + $expTime;
            $record->rushTimes = $record->rushTimes + 1;
            $record->save();

            $success = true;
        });

        if ($success) {

            // 记录匹配情况
            $this->recordMatch($lawyerId, $consultingId, Consulting::MATCH_TYPE_RANDOM);

            // 记录匹配次数
            $this->incMatchTimes($lawyerId);

            // 律师加入聊天室事件
            event('ConsultingLawyerJoined', $consulting);

            // 添加任务，指定时间后重新加入抢单池
            $job_data = ['id' => $consulting->id];
            Queue::later($expTime + 3, ConsultingRushExp::class, $job_data);

            // 延迟统计律师咨询数
            $job_data = ['id' => $lawyerId];
            Queue::later($expTime + 10, LawyerResponseStatistics::class, $job_data);

            // 如果已提问，那么推送公众号模板消息
            if ($consulting->isAsked == 1) {
                $pagepath = 'pages/lawyer/lawyer?confirm_id=' . $consulting->id;
                $userName = Member::where('id', $consulting->memberId)->value('nickname');
                $this->getSubscriptionMessageService()->messageFreeConsulting($lawyer->id, $userName, $consulting->ask, $now, $expTime, $pagepath);
            }
        }

        return true;
    }

    /**
     * 抢单+确认接单
     * @param Consulting $consulting
     * @param $lawyerId
     * @param bool $confirm 是否直接确认
     * @return bool
     */
    public function matchRush(Consulting $consulting, $lawyerId, bool $confirm = true): bool
    {
        $consultingId = $consulting->id;

        $success = false;
        $record = null;

        // 抢单过期时间
        $expTime = Config::get('consulting.rush_time');

        $signature = Consulting::class . $consultingId;
        $this->getSynchronizer()->synchronized($signature, function () use ($consultingId, $lawyerId, $expTime, $confirm, &$record, &$success) {

            $record = Consulting::where('id', $consultingId)->find();

            if (is_id($record->lawyerId)) {
                return;
            }

            if ($record->rushStatus == 2) {
                return;
            }

            $now = time();

            $rushStatus = $confirm ? 2 : 1;

            $record->lawyerId = $lawyerId;
            $record->rushStatus = $rushStatus;
            $record->rushTime = $now;
            $record->rushExpTime = $now + $expTime;
            $record->rushTimes = $record->rushTimes + 1;
            $record->save();

            $success = true;
        });

        if ($success) {

            // 如果咨询是在抢单匹配状态，记录本次抢单
            if ($record->matchType == Consulting::MATCH_TYPE_RUSH) {
                $this->recordMatch($lawyerId, $consultingId, $record->matchType);
            }

            // 律师加入聊天室事件
            event('ConsultingLawyerJoined', $record);

            // 未确认的，添加任务，指定时间后重新加入抢单池
            if (!$confirm) {
                $job_data = ['id' => $record->id];
                Queue::later($expTime + 3, ConsultingRushExp::class, $job_data);
            }

            // 延迟统计律师咨询数
            $job_data = ['id' => $lawyerId];
            Queue::later($expTime + 10, LawyerResponseStatistics::class, $job_data);

            // 自己抢单不发公众号模板消息
        }

        return $success;
    }

    /**
     * 确认接单
     * @param Consulting $consulting
     * @param $lawyerId
     * @return bool
     */
    public function confirm(Consulting $consulting, $lawyerId): bool
    {

        $consultingId = $consulting->id;
        $success = false;

        $signature = Consulting::class . $consultingId;
        $this->getSynchronizer()->synchronized($signature, function () use ($consultingId, $lawyerId, &$record, &$success) {

            $record = Consulting::where('id', $consultingId)->find();
            if (!$record) {
                throw new ValidateException('该咨询不存在或已被删除');
            }

            if (!is_id($record->lawyerId) || $record->lawyerId != $lawyerId) {
                throw new ValidateException('该咨询已重新分配');
            }

            if ($record->rushStatus == 2) {
                return;
            }

            $record->lawyerId = $lawyerId;
            $record->rushStatus = 2;
            $record->save();

            $success = true;
        });

        return $success;
    }

    /**
     * 重新进入抢单池
     * @param Consulting $consulting
     * @return bool
     */
    public function backToPool(Consulting $consulting): bool
    {
        $consultingId = $consulting->id;

        $success = false;

        $signature = Consulting::class . $consultingId;
        $this->getSynchronizer()->synchronized($signature, function () use ($consultingId, &$success) {

            $record = Consulting::where('id', $consultingId)->find();

            if (is_numeric($record->rushExpTime) && $record->rushExpTime > time()) {
                return;
            }

            if ($record->rushStatus == 2) {
                return;
            }

            // 接单方式变成 律师抢单
            $record->matchType = Consulting::MATCH_TYPE_RUSH;
            $record->lawyerId = 0;
            $record->rushStatus = 0;
            $record->rushTime = null;
            $record->rushExpTime = null;
            $record->save();

            $success = true;
        });

        return $success;
    }

    /**
     * 获取随机匹配的律师
     * @param Consulting $consulting
     * @return Member|null
     */
    private function getRandLawyer(Consulting $consulting): ?Member
    {
        // 我的账号一定匹配测试律师
        if ($consulting->memberId == '22155') {
            return Member::where('id', '248208')->find();
        }

        // 排除的律师
        $excludedLawyerIds = [];

        // 如果该律师已被分配了同问题下的咨询，那么不匹配他
        if (is_id($consulting->questionId)) {
            $excludedLawyerIds = Consulting::where('questionId', $consulting->questionId)->column('lawyerId');
        }

        // 不匹配测试账号
        $tests = Config::get('member.testAccount', []);

        // 查询一个律师
        return $this->getMemberDao()->findLawyerRand($consulting->cityId, $consulting->provinceId, $excludedLawyerIds, $tests);
    }

    private function getSynchronizer(): Synchronizer
    {
        return app(Synchronizer::class);
    }

    private function getSubscriptionMessageService(): SubscriptionMessageService
    {
        return app(SubscriptionMessageService::class);
    }

    private function getMemberDao(): MemberDao
    {
        return app(MemberDao::class);
    }
}
